//! percpu 变量定义

use core::{mem::size_of, ptr::null_mut};

use crate::{
    processor::nr_cpus,
    space::kalloc::{kfree, kmalloc, GfpFlags},
};

/// percpu 数据
pub struct PercpuData<T> {
    offset: *mut T,
    initialized: bool,
}

impl<T> Default for PercpuData<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T> PercpuData<T> {
    /// 构造数据
    pub const fn new() -> Self {
        Self { offset: null_mut(), initialized: false }
    }

    /// 初始化percpu数据
    ///
    /// percpu 需要初始化才能使用
    ///
    /// # Panics
    /// 内存不足将会 panic
    pub fn init_perdata(&mut self, p: T) {
        assert!(!self.initialized);
        let off = kmalloc(nr_cpus() * size_of::<T>(), GfpFlags::ZERO_NORMAL).unwrap();
        self.offset = off as *mut T;
        self.initialized = true;

        unsafe {
            for i in 0..nr_cpus() {
                let ptr = self.offset.add(i);
                ptr.copy_from(&p, 1);
            }
        }
    }
}

impl<T> Drop for PercpuData<T> {
    fn drop(&mut self) {
        if self.initialized {
            kfree(self.offset as usize);
        }
    }
}

impl<T> PercpuData<T> {
    /// 获取对应 cpu 的 perdata
    ///
    /// # Panics
    /// index 超过 cpu 实例或者 perdata 没有初始化将会 panic
    pub fn get(&self, index: usize) -> &T {
        assert!(index < nr_cpus() && self.initialized);
        unsafe { &*(self.offset.add(index)) }
    }

    /// 获取对应 cpu 的 可变 perdata
    ///
    /// # Panics
    /// index 超过 cpu 实例或者 perdata 没有初始化将会 panic
    pub fn get_mut(&mut self, index: usize) -> &mut T {
        assert!(index < nr_cpus() && self.initialized);
        unsafe { &mut *(self.offset.add(index)) }
    }
}
